# Copyright (c) 2018 Presto Labs Pte. Ltd.
# Author: jshin

from coin.strategy.mm.subscription import SubscriptionRequest
from coin.exchange.bitfinex_v2.order_gateway import BitfinexV2OrderGateway
from coin.exchange.bitfinex_v2.order_gateway_margin import (BitfinexV2OrderGatewayMargin)
from coin.strategy.executor.aggressive.interface import AggressiveExecutorInterface
from coin.strategy.executor.passive.interface import PassiveExecutorInterface
from coin.strategy.mm.binance.simple_mm_executor import (BinanceSimpleMMExecutor,
                                                         BinanceSimpleAggExecutor)
from coin.strategy.mm.binance_futures_swap.simple_mm_executor import (
    BinanceSwapFuturesSimpleMMExecutor,
    BinanceSwapFuturesSimpleAggExecutor)
from coin.strategy.mm.bitfinex.simple_mm_executor import (BitfinexSimpleMMExecutor)
from coin.strategy.mm.bitfinex.simple_mm_executor_margin import (BitfinexMarginSimpleMMExecutor)
from coin.strategy.mm.bitflyer_v1.simple_mm_executor2 import (BitflyerSimpleMMExecutor,
                                                              BitflyerSimpleAggExecutor)
from coin.strategy.mm.bithumb.simple_mm_executor import (BithumbSimpleMMExecutor,
                                                         BithumbSimpleAggExecutor)
from coin.strategy.mm.bitmex.simple_mm_executor_4 import (BitmexSimpleMMExecutor,
                                                          BitmexSimpleAggExecutor)
from coin.strategy.mm.hitbtc_v2.simple_mm_executor import HitbtcSimpleMMExecutor
from coin.strategy.mm.huobi.simple_mm_executor import (HuobiSimpleMMExecutor,
                                                       HuobiSimpleAggExecutor)
from coin.strategy.mm.huobi_futures.simple_mm_executor import (HuobiFuturesSimpleMMExecutor,
                                                               HuobiFuturesSimpleAggExecutor)
from coin.strategy.mm.kraken.simple_mm_executor import (KrakenSimpleMMExecutor,
                                                        KrakenSimpleAggExecutor)
from coin.strategy.mm.kraken_futures.simple_mm_executor import (KrakenFuturesSimpleMMExecutor,
                                                                KrakenFuturesSimpleAggExecutor)
from coin.strategy.mm.okex.simple_mm_executor import (OkexSimpleMMExecutor,
                                                      OkexSimpleAggExecutor,
                                                      OkexSimpleMMExecutorV3,
                                                      OkexSimpleAggExecutorV3)
from coin.strategy.mm.okex_futures.simple_mm_executor_4 import (OkexFuturesSimpleMMExecutor,
                                                                OkexFuturesSimpleAggExecutor,
                                                                OkexFuturesSimpleMMExecutorV3,
                                                                OkexFuturesSimpleAggExecutorV3,
                                                                OkexFuturesSwapSimpleAggExecutor)
from coin.strategy.mm.okex_futures_swap.simple_mm_executor import OkexFuturesSwapSimpleMMExecutor
from coin.strategy.mm.quoinex_v2.simple_mm_executor import (QuoinexSimpleMMExecutor,
                                                            QuoinexSimpleAggExecutor)
from coin.strategy.mm.upbit.simple_mm_executor import (UpbitSimpleMMExecutor,
                                                       UpbitSimpleAggExecutor)
from coin.strategy.mm.gdax.simple_mm_executor import (GdaxSimpleMMExecutor, GdaxSimpleAggExecutor)
from coin.strategy.mm.bitstamp_v2.simple_mm_executor import (BitstampSimpleMMExecutor,
                                                             BitstampSimpleAggExecutor)
from coin.strategy.mm.korbit_v1.simple_mm_executor import (KorbitSimpleMMExecutor,
                                                           KorbitSimpleAggExecutor)
from coin.strategy.mm.coinone_v2.simple_mm_executor import (CoinoneSimpleMMExecutor,
                                                            CoinoneSimpleAggExecutor)
from coin.strategy.mm.gopax.simple_mm_executor import (GopaxSimpleMMExecutor,
                                                       GopaxSimpleAggExecutor)
from coin.strategy.mm.deribit_v2.simple_mm_executor import (DeribitFuturesSimpleMMExecutor,
                                                            DeribitFuturesSimpleAggExecutor)
from coin.strategy.mm.uniswap_v3.simple_mm_executor import (
    UniswapV3SimpleAggExecutor,
)
from coin.strategy.mm.uniswap_v3_arbitrum.simple_mm_executor import (
    UniswapV3ArbitrumSimpleAggExecutor,
)
from coin.strategy.mm.uniswap_v2.simple_mm_executor import (
    UniswapV2SimpleAggExecutor,
)


def construct_mm_executor(req, param, og, logger, *, latency_recorder=None):
  req = SubscriptionRequest.from_any(req)

  if req.exchange == "Binance":
    if req.market_type == 'Spot':
      executor_cls = BinanceSimpleMMExecutor
    elif req.market_type == 'Futures':
      executor_cls = BinanceSwapFuturesSimpleMMExecutor
    else:
      raise ValueError('Unknown Binance market type %s.' % req)
  elif req.exchange == "Bitfinex":
    if isinstance(og, BitfinexV2OrderGateway):
      executor_cls = BitfinexSimpleMMExecutor
    elif isinstance(og, BitfinexV2OrderGatewayMargin):
      executor_cls = BitfinexMarginSimpleMMExecutor
    else:
      raise ValueError('Unknown og type %s' % og)
  elif req.exchange == "Bithumb":
    executor_cls = BithumbSimpleMMExecutor
  elif req.exchange == 'Huobi':
    if req.market_type == 'Spot':
      executor_cls = HuobiSimpleMMExecutor
    elif req.market_type == 'Futures':
      executor_cls = HuobiFuturesSimpleMMExecutor
    else:
      raise ValueError('Unknown market type for Huobi %s', req.market_type)
  elif req.exchange == 'Okex' and req.market_type == 'Futures':
    if req.api_version in (None, 'v3'):
      executor_cls = OkexFuturesSimpleMMExecutorV3
    elif req.api_version == 'v3_swap':
      executor_cls = OkexFuturesSwapSimpleMMExecutor
    elif req.api_version == 'v1':
      executor_cls = OkexFuturesSimpleMMExecutor
    else:
      raise ValueError('Invalid Okex futures api version %s.' % req.api_version)
  elif req.exchange == 'Okex' and req.market_type == 'Spot':
    if req.api_version in (None, 'v3'):
      executor_cls = OkexSimpleMMExecutorV3
    elif req.api_version == 'v1':
      executor_cls = OkexSimpleMMExecutor
    else:
      raise ValueError('Invalid Okex spot api version %s.' % req.api_version)
  elif req.exchange == 'Upbit':
    executor_cls = UpbitSimpleMMExecutor
  elif req.exchange == 'Kraken':
    if req.market_type == 'Spot':
      executor_cls = KrakenSimpleMMExecutor
    elif req.market_type == 'Futures':
      executor_cls = KrakenFuturesSimpleMMExecutor
    else:
      raise ValueError('Invalid kraken market type %s!' % req.market_type)
  elif req.exchange == 'Deribit':
    if req.market_type == 'Futures':
      executor_cls = DeribitFuturesSimpleMMExecutor
    else:
      raise ValueError('Invalid kraken market type %s!' % req.market_type)
  elif req.exchange == 'Bitflyer':
    executor_cls = BitflyerSimpleMMExecutor
  elif req.exchange == 'Bitmex':
    executor_cls = BitmexSimpleMMExecutor
  elif req.exchange == 'Quoinex':
    executor_cls = QuoinexSimpleMMExecutor
  elif req.exchange == 'Hitbtc':
    executor_cls = HitbtcSimpleMMExecutor
  elif req.exchange == 'Gdax':
    executor_cls = GdaxSimpleMMExecutor
  elif req.exchange == 'Bitstamp':
    executor_cls = BitstampSimpleMMExecutor
  elif req.exchange == 'Korbit':
    executor_cls = KorbitSimpleMMExecutor
  elif req.exchange == 'Coinone':
    executor_cls = CoinoneSimpleMMExecutor
  elif req.exchange == 'Gopax':
    executor_cls = GopaxSimpleMMExecutor
  else:
    raise ValueError()

  executor = executor_cls(param, og, logger=logger, latency_recorder=latency_recorder)
  assert isinstance(executor, PassiveExecutorInterface), type(executor)
  return executor


def construct_agg_executor(req, param, og, logger, *, latency_recorder=None):
  req = SubscriptionRequest.from_any(req)

  if req.exchange == 'Binance':
    if req.market_type == 'Spot':
      executor_cls = BinanceSimpleAggExecutor
    elif req.market_type == 'Futures':
      executor_cls = BinanceSwapFuturesSimpleAggExecutor
    else:
      raise ValueError('Unknown Binance market type! %s' % req)
  elif req.exchange == "Bitmex":
    executor_cls = BitmexSimpleAggExecutor
  elif req.exchange == 'Bithumb':
    executor_cls = BithumbSimpleAggExecutor
  elif req.exchange == 'Huobi':
    if req.market_type == 'Spot':
      executor_cls = HuobiSimpleAggExecutor
    elif req.market_type == 'Futures':
      executor_cls = HuobiFuturesSimpleAggExecutor
    else:
      raise ValueError('Unknown market type for Huobi %s', req.market_type)
  elif req.exchange == 'Okex' and req.market_type == 'Futures':
    if req.api_version in (None, 'v3'):
      executor_cls = OkexFuturesSimpleAggExecutorV3
    elif req.api_version == 'v3_swap':
      executor_cls = OkexFuturesSwapSimpleAggExecutor
    elif req.api_version == 'v1':
      executor_cls = OkexFuturesSimpleAggExecutor
    else:
      raise ValueError('Invalid Okex futures api version %s.' % req.api_version)
  elif req.exchange == 'Okex' and req.market_type == 'Spot':
    if req.api_version in (None, 'v3'):
      executor_cls = OkexSimpleAggExecutorV3
    elif req.api_version == 'v1':
      executor_cls = OkexSimpleAggExecutor
    else:
      raise ValueError('Invalid Okex spot api version %s.' % req.api_version)
  elif req.exchange == 'Upbit':
    executor_cls = UpbitSimpleAggExecutor
  elif req.exchange == 'Gdax':
    executor_cls = GdaxSimpleAggExecutor
  elif req.exchange == 'Bitstamp':
    executor_cls = BitstampSimpleAggExecutor
  elif req.exchange == 'Korbit':
    executor_cls = KorbitSimpleAggExecutor
  elif req.exchange == 'Coinone':
    executor_cls = CoinoneSimpleAggExecutor
  elif req.exchange == 'Gopax':
    executor_cls = GopaxSimpleAggExecutor
  elif req.exchange == 'Bitflyer':
    executor_cls = BitflyerSimpleAggExecutor
  elif req.exchange == 'Quoinex':
    executor_cls = QuoinexSimpleAggExecutor
  elif req.exchange == 'Uniswap' and req.api_version == 'v3':
    executor_cls = UniswapV3SimpleAggExecutor
  elif req.exchange == 'Uniswap' and req.api_version == 'v3-arbitrum':
    executor_cls = UniswapV3ArbitrumSimpleAggExecutor
  elif req.exchange == 'Uniswap' and req.api_version == 'v2':
    executor_cls = UniswapV2SimpleAggExecutor
  elif req.exchange == 'Kraken':
    if req.market_type == 'Spot':
      executor_cls = KrakenSimpleAggExecutor
    elif req.market_type == 'Futures':
      executor_cls = KrakenFuturesSimpleAggExecutor
    else:
      raise ValueError('Invalid kraken market type! %s', req.market_type)
  elif req.exchange == 'Deribit':
    if req.market_type == 'Futures':
      executor_cls = DeribitFuturesSimpleAggExecutor
    else:
      raise ValueError('Invalid kraken market type %s!' % req.market_type)
  else:
    raise ValueError()

  executor = executor_cls(param, og, logger=logger, latency_recorder=latency_recorder)
  assert isinstance(executor, AggressiveExecutorInterface), type(executor)
  return executor
